package ex205n.exp;

import java.util.Stack;

public class ArithmeticExpression {
    // 表示运算符的正则表达式
    private final static String OPERATOR = "[\\+\\-\\*/]";

    private String expression;             // 表达式字符串
    private int result;                    // 表达式的运算结果
    private Stack<Integer> operandStack;   // 运算数栈
    private Stack<String> operatorStack;   // 运算符栈

    /**
     * 创建一个计算指定算术表达式的对象
     * 使用方式：
     * <pre>
     *    ArithmeticExpression exp = new ArithmeticExpression(表达式字符串);
     *    exp.compute();
     *    int n = exp.getResult();
     * </pre>
     *
     * @param expression 指定的算术表达式String
     */
    public ArithmeticExpression(String expression) {
        this.expression = expression;
        operandStack = new Stack<>();
        operatorStack = new Stack<>();
    }

    /**
     * 计算 expression 的值，结果存放到 result
     */
    public void compute() {
        // 预处理表达式字符串，分解为运算数和运算符的组成的String数组
        String[] symbols = this.getRegularExpression().split(" ");

        // 依次处理每个运算符和运算数
        // 每次循环, symbol 存放从表达式中刚刚取出的符号，可能是运算符、运算数或括号
        for (String symbol : symbols) {
            if ("+".equals(symbol) || "-".equals(symbol) || "*".equals(symbol) || "/".equals(symbol)) {
                // 运算符分支
                // 条件也可以写成正则表达式形式: symbol.matches(OPERATOR)
                while (!operatorStack.isEmpty() && getPrior(operatorStack.peek()) > getPrior(symbol)) {
                    this.processTopOperator(); // 计算栈顶的运算符
                }
                operatorStack.push(symbol); // 当前运算符入栈
            } else if (")".equals(symbol)) {
                // 右括号分支
                while (!"(".equals(operatorStack.peek())) {
                    processTopOperator();  // 计算栈顶的运算符
                }
                operatorStack.pop();       // 弹出栈顶的(
            } else if ("(".equals(symbol)) {
                // 左括号分支
                operatorStack.push(symbol); // 左括号入栈
            } else {
                // 运算数分支，非以上分支一定是运算数
                operandStack.push(Integer.valueOf(symbol));  // 运算数入栈，运算数栈
            }
        }

        // 全部入栈后，处理运算符栈中的所有运算符
        while (!operatorStack.isEmpty()) {
            processTopOperator();
        }

        result = operandStack.pop(); // 最后运算数栈的栈顶就是结果
    }

    /**
     * 返回自定义的运算符优先级
     *
     * @param operator 运算符 + - * /
     * @return +和-返回1，*和/返回2, 其他错误符号返回-1ß
     */
    private int getPrior(String operator) {
        return switch (operator) {
            case "+", "-" -> 1;
            case "*", "/" -> 2;
            default -> -1;
        };
    }

    /**
     * 处理栈顶的运算符
     */
    private void processTopOperator() {
        String operator = operatorStack.pop(); // 弹出栈顶运算符
        int operand1 = operandStack.pop();     // 弹出栈顶运算数
        int operand2 = operandStack.pop();     // 弹出栈顶运算数

        int result = switch (operator) {
            case "+" -> operand2 + operand1;
            case "-" -> operand2 - operand1;
            case "*" -> operand2 * operand1;
            case "/" -> operand2 / operand1;
            default -> 0;
        };
        operandStack.push(result); // 运算结果入运算数栈
    }

    /**
     * 对表达式字符串进行预处理，生成符合题目要求的字符串
     *
     * @return 规范的表达式字符串
     */

    public String getRegularExpression() {
        String result = null;

        result = expression.replaceAll("[\\s]+", ""); // 去除所有空白字符
        result = result.replaceAll("\\*", " * ");     // * 两边各加1个空格
        result = result.replaceAll("\\+", " + ");     // + 两边各加1个空格
        result = result.replaceAll("\\-", " - ");     // - 两边各加1个空格
        result = result.replaceAll("/", " / ");       // / 两边各加1个空格
        result = result.replaceAll("\\(", "( ");      // ( 右边加1个空格
        result = result.replaceAll("\\)", " )");      // ) 左边加1个空格

        return result;
    }

    // setters & getters -------
    public String getExpression() {
        return expression;
    }

    public void setExpression(String expression) {
        this.expression = expression;
    }

    public int getResult() {
        return result;
    }

}
